package com.ssy.lingxi.contract.serviceimpl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.contract.ContractSourceTypeEnum;
import com.ssy.lingxi.common.constant.product.GoodsPriceStatusEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.common.utils.DateTimeUtil;
import com.ssy.lingxi.component.rabbitMQ.service.IRabbitMQUtils;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.component.redis.utils.SerialNumberUtils;
import com.ssy.lingxi.contract.api.model.constant.ContractConstants;
import com.ssy.lingxi.contract.api.model.vo.request.ContractOrderAmountLogRequest;
import com.ssy.lingxi.contract.entity.*;
import com.ssy.lingxi.contract.model.bo.sign.SignFlowBO;
import com.ssy.lingxi.contract.model.constant.*;
import com.ssy.lingxi.contract.model.vo.common.response.ContractPayPlanVO;
import com.ssy.lingxi.contract.model.vo.common.response.ContractTextVO;
import com.ssy.lingxi.contract.model.vo.common.response.PageItemVO;
import com.ssy.lingxi.contract.model.vo.common.response.TaskStepVO;
import com.ssy.lingxi.contract.model.vo.manage.request.*;
import com.ssy.lingxi.contract.model.vo.manage.response.*;
import com.ssy.lingxi.contract.repository.ContractOrderAmountLogRepository;
import com.ssy.lingxi.contract.repository.ContractRepository;
import com.ssy.lingxi.contract.service.*;
import com.ssy.lingxi.dto.response.CorporateAccountConfigResponse;
import com.ssy.lingxi.message.api.constant.MessageTemplateCode;
import com.ssy.lingxi.message.api.vo.request.SystemMessageRequest;
import com.ssy.lingxi.product.api.model.request.price.ContractPriceRequest;
import com.ssy.lingxi.product.api.model.request.price.MaterielPriceRequest;
import com.ssy.lingxi.report.api.enums.ContractCoordinationOperateTypeEnum;
import com.ssy.lingxi.report.api.enums.ContractManageOperateTypeEnum;
import com.ssy.lingxi.report.api.enums.OperateDataSourceEnum;
import com.ssy.lingxi.report.api.vo.request.OperateNoticeVO;
import com.ssy.lingxi.workflow.api.model.contant.ProcessEnum;
import com.ssy.lingxi.workflow.api.model.contant.ProcessTaskStatusEnum;
import com.ssy.lingxi.workflow.api.model.vo.response.SimpleTaskCompleteVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 合同管理接口实现
 * @author 伍将
 * @date 2021/2/2
 * @version 2.0.0
 */
@Service
@Slf4j
public class ContractManageServiceImpl implements IContractManageService {

    @Resource
    private ContractRepository repository;

    @Resource
    private IContractInnerRecordService innerRecordService;

    @Resource
    private IContractOuterRecordService outerRecordService;

    @Resource
    private IContractPayPlanService payPlanService;

    @Resource
    private IContractTextService contractTextService;

    @Resource
    private IContractPurchaseMaterielService purchaseMaterielService;

    @Resource
    private IFeignService feignService;

    @Resource
    private IPurchaseInquiryService purchaseInquiryService;

    @Resource
    private IPurchaseInviteBidService purchaseInviteBidService;

    @Resource
    private IPurchaseViePriceService purchaseViePriceService;
    @Resource
    private ContractOrderAmountLogRepository contractOrderAmountLogRepository;
    @Resource
    private IContractPurchaseMaterielService contractPurchaseMaterielService;
    @Resource
    private IRedisStringUtils redisStringUtils;
    @Resource
    private IContractSignatureService contractSignatureService;
    @Resource
    private IProcessFeignService processFeignService;
    @Resource
    private IRabbitMQUtils rabbitMQUtils;
    /**
     * 分页查询合同列表
     * @author 伍将
     * @date 2021/2/2
     * @param user: 当前登录用户
     * @param pageVO: 分页请求参数
     * @return 操作结果
     **/
    @Override
    public Wrapper<PageData<ContractManageQueryVO>> pageList(UserLoginCacheDTO user, ContractManagePageVO pageVO) {
        return commonPageList(user, pageVO, null, null);
    }

    /**
     * 校验合同归属，甲方或乙方
     * @param user 登录用户
     * @param contract 合同
     * @return 交易结果
     */
    private boolean verifyContractAndUser(UserLoginCacheDTO user,ContractDO contract){
        boolean result=true;
        if(!(contract.getPartyAMemberId().equals(user.getMemberId())&&contract.getPartyARoleId().equals(user.getMemberRoleId()))
            &&!(contract.getPartyBMemberId().equals(user.getMemberId())&&contract.getPartyBRoleId().equals(user.getMemberRoleId()))){
            result=false;
        }
        return result;
    }

    /**
     * 查询合同详情
     * @author 伍将
     * @date 2021/2/7
     * @param user: 当前登录用户
     * @param contractId: 合同id
     * @return 操作结果
     **/
    @Override
    public Wrapper<ContractManageDetailVO> getDetail(UserLoginCacheDTO user, Long contractId) {

        ContractManageDetailVO result = new ContractManageDetailVO();

        ContractDO contract = repository.findById(contractId).orElse(null);
        if (null == contract) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
        }
        //校验合同归属
        if(!verifyContractAndUser(user,contract)){
            throw new BusinessException(ResponseCode.DATA_AUTH_NOT_ALLOWED);
        }

        // 获取合同基本信息
        ContractManageBasicsVO basics = new ContractManageBasicsVO();
        basics.setId(contractId);
        basics.setContractNo(contract.getContractNo());
        basics.setOuterStatus(contract.getOuterStatus());
        basics.setOuterStatusName(ContractOuterStatusEnum.getMessage(contract.getOuterStatus()));
        basics.setInnerStatus(contract.getPartyAInnerStatus());
        basics.setInnerStatusName(ContractPartyAInnerStatusEnum.getMessage(contract.getPartyAInnerStatus()));
        basics.setContractAbstract(contract.getContractAbstract());
        basics.setSourceType(contract.getSourceType());
        basics.setSourceTypeName(ContractSourceTypeEnum.getNameByCode(contract.getSourceType()));
        basics.setSourceId(contract.getSourceId());
        basics.setSourceNo(contract.getSourceNo());
        basics.setPartyBName(contract.getPartyBName());
        basics.setTotalAmount(contract.getTotalAmount());
        basics.setStartTime(DateUtil.format(DateUtil.date(contract.getStartTime()), "yyyy-MM-dd"));
        basics.setEndTime(DateUtil.format(DateUtil.date(contract.getEndTime()), "yyyy-MM-dd"));
        basics.setPartyBMemberId(contract.getPartyBMemberId());
        basics.setPartyBRoleId(contract.getPartyBRoleId());
        basics.setSignatureLogId(contract.getSignatureLogId());
        basics.setTurn(contract.getTurn());
        result.setBasics(basics);

        // 获取外部流转步骤
        List<TaskStepVO> outerTaskList =processFeignService.listTaskStepOut(contract.getOuterTaskType(),contract.getOuterTaskId(),contract.getPartyARoleId(),contract.getPartyBRoleId());
        result.setOuterTaskStepList(outerTaskList);

        // 获取内部流转步骤
        List<TaskStepVO> innerTaskList =processFeignService.listTaskStep(contract.getPartyAInnerTaskType(),contract.getPartyAInnerTaskId(),contract.getPartyAMemberId());
        result.setInnerTaskStepList(innerTaskList);

        // 获取支付计划
        Wrapper<List<ContractPayPlanVO>> payPlanListWrapper = payPlanService.getPayPlanList(contractId);
        if (null == payPlanListWrapper || payPlanListWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            throw new BusinessException(ResponseCode.CONTRACT_GET_PAY_PLAN_ERROR);
        }
        result.setPayPlanList(payPlanListWrapper.getData());

        // 获取合同文本
        Wrapper<ContractTextVO> textWrapper = contractTextService.getDetail(contractId);
        if (null == textWrapper || textWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            throw new BusinessException(ResponseCode.CONTRACT_GET_OUTER_RECORD_ERROR);
        }
        result.setContractText(textWrapper.getData());

        return Wrapper.success(result);
    }

    /**
     * 更新需求单状态
     * @param user
     * @param sourceType
     * @param sourceId
     * @param partbMemberId
     * @param partbRoleId
     * @param isCreate
     */
    private void updateSourceInfo(UserLoginCacheDTO user,Integer sourceType,Long sourceId,Long partbMemberId,Long partbRoleId,CommonBooleanEnum isCreate){
        if(sourceId==null){return;}
        // 更新采购询价合同已被创建合同
        if (ContractSourceTypeEnum.PURCHASE_INQUIRY.getCode().equals(sourceType)) {
           purchaseInquiryService.updateCreateStatus(user, sourceId, partbMemberId, partbRoleId, isCreate.getCode());
        }
        // 更新采购招标合同已被创建合同
        else if (ContractSourceTypeEnum.PURCHASE_INVITE_BID.getCode().equals(sourceType)) {
            purchaseInviteBidService.updateCreateStatus(user, sourceId, partbMemberId, partbRoleId, isCreate.getCode());
        }
        // 更新采购竞价合同已被创建合同
        else if (ContractSourceTypeEnum.PURCHASE_VIA_PRICE.getCode().equals(sourceType)) {
            purchaseViePriceService.updateCreateStatus(user, sourceId, partbMemberId, partbRoleId, isCreate.getCode());
        }
    }

    /**
     * 合同作废
     * @author 伍将
     * @date 2021/2/7
     * @param user: 当前登录用户
     * @param invalidVO: 作废请求参数
     * @return 操作结果
     **/
    @Transactional
    @Override
    public Wrapper<Void> invalid(UserLoginCacheDTO user, ContractManageInvalidVO invalidVO) {

        ContractDO contract = repository.findById(invalidVO.getContractId()).orElse(null);
        if (null == contract) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
        }

        // 除已停用、已作废、合同已到期状态之外的所有状态的合同都可以作废
        if (ContractOuterStatusEnum.STOP_USING.getCode().equals(contract.getOuterStatus())
                || ContractOuterStatusEnum.INVALID.getCode().equals(contract.getOuterStatus())
                || ContractOuterStatusEnum.EXPIRE.getCode().equals(contract.getOuterStatus())) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_INVALID);
        }

        contract.setInvalidTime(DateUtil.parse(invalidVO.getInvalidTime()).getTime());
        contract.setInvalidReason(invalidVO.getInvalidReason());
        contract.setOuterStatus(ContractOuterStatusEnum.INVALID.getCode());
        contract.setPartyAInnerStatus(ContractPartyAInnerStatusEnum.INVALID.getCode());
        contract.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(contract);

        //更新询价、竞价、报价单状态
        updateSourceInfo(user,contract.getSourceType(),contract.getSourceId(),contract.getPartyBMemberId(),contract.getPartyBRoleId(),CommonBooleanEnum.NO);

        // 新增外部流转记录
        outerRecordService.add(user, contract.getId(), contract.getOuterStatus(), ContractOuterStatusEnum.INVALID.getMessage(), ContractActionEnum.INVALID.getMessage(), invalidVO.getInvalidReason());

        //合同作废通知商品服务
        notifyProductServiceForContractChange(GoodsPriceStatusEnum.ABANDONED,contract);
        return Wrapper.success();
    }

    /**
     * 保存合同
     * @author 伍将
     * @date 2021/2/24
     * @param user: 当前登录用户
     * @param saveVO: 保存参数
     * @return 操作结果
     **/
    @Transactional
    @Override
    public Wrapper<Long> save(UserLoginCacheDTO user, ContractUpdateVO saveVO) {

        // 验证合同有效时间：终止日期要大于起始日期
        long startTime = DateUtil.parse(saveVO.getBasicsVO().getStartTime()).getTime();
        long endTime = DateUtil.parse(saveVO.getBasicsVO().getEndTime()).getTime();
        if (startTime >= endTime) {
            throw new BusinessException(ResponseCode.CONTRACT_START_TIME_GREATER_THAN_END_TIME);
        }

        // 验证采购物料信息
        double bidAmount = 0D;
        for (PurchaseMaterielUpdateVO item : saveVO.getPurchaseMaterielList()) {
            // 授标金额=单价*授标数量
            if (!item.getBidAmount() .equals(item.getPrice() * item.getBidCount())) {
                throw new BusinessException(ResponseCode.CONTRACT_BID_AMOUNT_ERROR);
            }
            bidAmount += item.getPrice() * item.getBidCount();
        }

        // 验证授标总金额是否等于各物料授标金额之和
        if (!saveVO.getBasicsVO().getTotalAmount().equals(bidAmount)) {
            throw new BusinessException(ResponseCode.CONTRACT_BID_AMOUNT_ERROR);
        }

        // 验证付款计划，付款次数不能重复
        Map<Integer, Long> payPlanNum = saveVO.getPayPlanList().stream().collect(Collectors.groupingBy(ContractPayPlanUpdateVO::getPayNum, Collectors.counting()));
        for (Integer key : payPlanNum.keySet()) {
            if (payPlanNum.get(key) > 1) {
                throw new BusinessException(ResponseCode.CONTRACT_PAY_NUM_REPEAT);
            }
        }

        // 验证付款计划
        for (ContractPayPlanUpdateVO item : saveVO.getPayPlanList()) {
            // 付款时间，大于或等于合同有效期的起始时间
            if (DateUtil.parse(item.getExpectPayTime()).getTime() < startTime) {
                throw new BusinessException(ResponseCode.CONTRACT_PAY_PLAN_EXPECT_PAY_TIME_ERROR);
            }
            // 账期付款，账期天数需大于0
            if (ContractPayWayEnum.PAYMENT_DAYS.getCode().equals(item.getPayWay()) && item.getPayParam() <= 0) {
                throw new BusinessException(ResponseCode.CONTRACT_PAY_PLAN_PAYMENT_DAYS_PARAM_ERROR);
            }
            // 月结付款，请款日期范围为1-31
            else if (ContractPayWayEnum.MONTH_SETTLEMENT.getCode().equals(item.getPayWay()) && (item.getPayParam() < 1 || item.getPayParam() > 31)) {
                throw new BusinessException(ResponseCode.CONTRACT_PAY_PLAN_MONTH_SETTLEMENT_PARAM_ERROR);
            }
        }
        Wrapper<Void> wrapper;

        boolean isHandmade=(saveVO.getBasicsVO().getSourceId()==null||saveVO.getBasicsVO().getSourceId()==0);
        //轮次
        Integer purchaseType=0;
        Double awardAmount=0D;
        // 变更合同id大于0，代表变更合同，需验证原合同
        boolean isChangeContract=ContractOperateTypeEnum.CHANGE_CONTRACT.getCode().equals(saveVO.getOperateType());
        ContractDO parentContract =null;
        if (isChangeContract) {
            parentContract = repository.findById(saveVO.getBasicsVO().getOldContractId()).orElse(null);
            if (null == parentContract) {
                throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
            }
            // 验证原合同创建人与当前登录用户身份是否相同
            if (!parentContract.getPartyAMemberId().equals(user.getMemberId()) || !parentContract.getPartyARoleId().equals(user.getMemberRoleId())) {
                throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_UPDATE);
            }
            // 外部状态为：已完成签约
            if (!ContractOuterStatusEnum.COMPLETE_SIGN_CONTRACT.getCode().equals(parentContract.getOuterStatus())) {
                throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_UPDATE);
            }
            if(parentContract.getSubContractId()!=null&&parentContract.getSubContractId()>0){
                throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_REPEAT_CHANGE);
            }
            // 变更合同时，合同编号、寻源类型、授标会员相关信息不可编辑，进行数据强转
            saveVO.getBasicsVO().setSourceId(parentContract.getSourceId());
            saveVO.getBasicsVO().setSourceType(parentContract.getSourceType());
            saveVO.getBasicsVO().setSourceNo(parentContract.getSourceNo());
            saveVO.getBasicsVO().setPartyBMemberId(parentContract.getPartyBMemberId());
            saveVO.getBasicsVO().setPartyBRoleId(parentContract.getPartyBRoleId());
            saveVO.getBasicsVO().setPartyBName(parentContract.getPartyBName());
            saveVO.getBasicsVO().setPurchaseType(parentContract.getPurchaseType());
            //清空物料id
            saveVO.getPurchaseMaterielList().forEach(o ->o.setId(null));
            //清空合同文本id
            saveVO.getContractText().setId(null);
        }
        // 验证合同编号是否唯一
        Boolean exists;
        ContractDO saveContract = convertContractDO(user, saveVO.getBasicsVO());
        boolean isSave=saveVO.getBasicsVO().getId() <= 0;//是否为新增
        // 基础信息id等于0时，代表新增
        if (isSave) {//新增合同
            // 验证合同编号是否唯一
            exists = repository.existsByContractNo(saveVO.getBasicsVO().getContractNo());
            if (exists) {
                throw new BusinessException(ResponseCode.CONTRACT_IS_EXIST);
            }
            if(!isHandmade&&!isChangeContract) {
                // 验证采购询价对应单据是否已被创建合同
                if (ContractSourceTypeEnum.PURCHASE_INQUIRY.getCode().equals(saveVO.getBasicsVO().getSourceType())) {
                    PurchaseInquiryDO purchaseInquiryDO = purchaseInquiryService.checkCreate(user, saveVO.getBasicsVO().getSourceId(), saveVO.getBasicsVO().getPartyBMemberId(), saveVO.getBasicsVO().getPartyBRoleId());
                    //授标金额
                    awardAmount = purchaseInquiryDO.getAwardAmount();
                    purchaseType=purchaseInquiryDO.getPurchaseType();
                    saveContract.setTurn(purchaseInquiryDO.getTurn());
                    saveContract.setSourceNo(purchaseInquiryDO.getDemandNO());
                    saveContract.setSupplierSourceId(purchaseInquiryDO.getQuotedPriceOrderId());
                    saveContract.setSupplierSourceNo(purchaseInquiryDO.getQuotedPriceOrderNo());
                }
                // 验证采购招标对应单据是否已被创建合同
                else if (ContractSourceTypeEnum.PURCHASE_INVITE_BID.getCode().equals(saveVO.getBasicsVO().getSourceType())) {
                    PurchaseInviteBidDO purchaseInviteBidDO = purchaseInviteBidService.checkCreate(user, saveVO.getBasicsVO().getSourceId(), saveVO.getBasicsVO().getPartyBMemberId(), saveVO.getBasicsVO().getPartyBRoleId());
                    //授标金额
                    awardAmount=purchaseInviteBidDO.getBidWinnerAmount();
                    purchaseType=purchaseInviteBidDO.getPurchaseType();
                    saveContract.setSourceNo(purchaseInviteBidDO.getInviteBidNO());
                    saveContract.setSupplierSourceId(purchaseInviteBidDO.getBidId());
                    saveContract.setSupplierSourceNo(purchaseInviteBidDO.getBidNo());
                }
                // 验证采购竞价对应单据是否已被创建合同
                else if (ContractSourceTypeEnum.PURCHASE_VIA_PRICE.getCode().equals(saveVO.getBasicsVO().getSourceType())) {
                    PurchaseViePriceDO viePriceDO = purchaseViePriceService.checkCreate(user, saveVO.getBasicsVO().getSourceId(), saveVO.getBasicsVO().getPartyBMemberId(), saveVO.getBasicsVO().getPartyBRoleId());
                    //授标金额
                    awardAmount = viePriceDO.getAwardAmount();
                    saveContract.setSourceNo(viePriceDO.getViePriceNO());
                    saveContract.setSupplierSourceId(viePriceDO.getQuotedPriceOrderId());
                    saveContract.setSupplierSourceNo(viePriceDO.getQuotedPriceOrderNo());
                }
            }
        } else {//更新合同
            // 验证合同编号是否唯一
            exists = repository.existsByContractNoAndIdNot(saveVO.getBasicsVO().getContractNo(), saveVO.getBasicsVO().getId());
            if (exists) {
                throw new BusinessException(ResponseCode.CONTRACT_IS_EXIST);
            }
            ContractDO oldContract = repository.findById(saveVO.getBasicsVO().getId()).orElse(null);
            if (null == oldContract) {
                throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
            }

            // 外部状态为：待提交乙方签订合同/乙方不同意签订合同/甲方不同意签订合同时才允许修改
            if (!ContractOuterStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode().equals(oldContract.getOuterStatus())
                    && !ContractOuterStatusEnum.PARTY_A_DISAGREE_SIGN_CONTRACT.getCode().equals(oldContract.getOuterStatus())
                    && !ContractOuterStatusEnum.PARTY_B_DISAGREE_SIGN_CONTRACT.getCode().equals(oldContract.getOuterStatus())) {
                throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_UPDATE);
            }

            // 当前登录用户必须与原有甲方身份一致
            if (!oldContract.getPartyAMemberId().equals(saveContract.getPartyAMemberId())
                    || !oldContract.getPartyARoleId().equals(saveContract.getPartyARoleId())) {
                throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_UPDATE);
            }

            // 修改时不允许修改合同编号、寻源类型及对应单据、授标会员
            saveContract.setId(oldContract.getId());
            saveContract.setContractNo(oldContract.getContractNo());
            saveContract.setSourceId(oldContract.getSourceId());
            saveContract.setSourceType(oldContract.getSourceType());
            saveContract.setSourceNo(oldContract.getSourceNo());
            saveContract.setSupplierSourceId(oldContract.getSupplierSourceId());
            saveContract.setSupplierSourceNo(oldContract.getSupplierSourceNo());
            saveContract.setPartyBMemberId(oldContract.getPartyBMemberId());
            saveContract.setPartyBRoleId(oldContract.getPartyBRoleId());
            saveContract.setPartyBName(oldContract.getPartyBName());
            saveContract.setCreateTime(oldContract.getCreateTime());
            //不允许更新归属父级合同
            saveContract.setOldContractId(oldContract.getOldContractId());
            //工作流信息
            saveContract.setOuterTaskId(oldContract.getOuterTaskId());
            saveContract.setOuterTaskStep(oldContract.getOuterTaskStep());
            saveContract.setOuterStatus(oldContract.getOuterStatus());
/*            if(ContractOperateTypeEnum.UPDATE.getCode().equals(saveVO.getOperateType())){
                saveContract.setOuterStatus(ContractOuterStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode());
                saveContract.setPartyAInnerStatus(ContractPartyAInnerStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode());
            }*/
        }
        //如果非手工录入,新增，非变更合同
        if(!isHandmade&&isSave&&!isChangeContract){
            // 验证授标总金额是否等于各物料授标金额之和
            if (!saveVO.getBasicsVO().getTotalAmount().equals(awardAmount)) {
                throw new BusinessException(ResponseCode.CONTRACT_BID_AMOUNT_ERROR);
            }
        }
        saveContract.setPurchaseType(purchaseType);
        // 保存基础信息
        repository.saveAndFlush(saveContract);
        //如果是变更合同，填充父合同中的子合同id
        if(parentContract!=null){
            parentContract.setSubContractId(saveContract.getId());
            repository.saveAndFlush(parentContract);
        }

        // 保存合同文本
        wrapper = contractTextService.save(saveContract.getId(), saveVO.getContractText());
        if (null == wrapper || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            throw new BusinessException(ResponseCode.CONTRACT_TEXT_SAVE_FAIL);
        }

        // 保存付款计划
        wrapper = payPlanService.save(saveContract.getId(), saveVO.getPayPlanList());
        if (null == wrapper || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            throw new BusinessException(ResponseCode.CONTRACT_PAY_PLAN_SAVE_FAIL);
        }

        // 保存采购物料
        wrapper = purchaseMaterielService.save(saveContract.getId(), saveVO.getPurchaseMaterielList());
        if (null == wrapper || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            throw new BusinessException(ResponseCode.CONTRACT_PURCHASE_MATERIEL_SAVE_FAIL);
        }

        // 保存合同（询价/竞价/招标）待创建合同-1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        if(!isHandmade&&isSave) {//非手工单，新增时更新对应单据状态
            //非变更合同，新增时修改对应单据属性[是否创建了合同]
            if(saveContract.getOldContractId()==0) {
                // 更新采购询价合同已被创建合同
                if (ContractSourceTypeEnum.PURCHASE_INQUIRY.getCode().equals(saveVO.getBasicsVO().getSourceType())) {
                    wrapper = purchaseInquiryService.updateCreateStatus(user, saveVO.getBasicsVO().getSourceId(), saveVO.getBasicsVO().getPartyBMemberId(), saveVO.getBasicsVO().getPartyBRoleId(), CommonBooleanEnum.YES.getCode());
                }
                // 更新采购招标合同已被创建合同
                else if (ContractSourceTypeEnum.PURCHASE_INVITE_BID.getCode().equals(saveVO.getBasicsVO().getSourceType())) {
                    wrapper = purchaseInviteBidService.updateCreateStatus(user, saveVO.getBasicsVO().getSourceId(), saveVO.getBasicsVO().getPartyBMemberId(), saveVO.getBasicsVO().getPartyBRoleId(), CommonBooleanEnum.YES.getCode());
                }
                // 更新采购竞价合同已被创建合同
                else if (ContractSourceTypeEnum.PURCHASE_VIA_PRICE.getCode().equals(saveVO.getBasicsVO().getSourceType())) {
                    wrapper = purchaseViePriceService.updateCreateStatus(user, saveVO.getBasicsVO().getSourceId(), saveVO.getBasicsVO().getPartyBMemberId(), saveVO.getBasicsVO().getPartyBRoleId(), CommonBooleanEnum.YES.getCode());
                }
                if (null == wrapper || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                    return Wrapper.fail(Optional.ofNullable(wrapper).orElse(Wrapper.fail(ResponseCode.BUSINESS_ERROR)) );
                }
            }
            // 保存合同（询价/竞价/招标）待创建合同-1
            noticeVO.setReduceCount(com.ssy.lingxi.contract.constant.Constants.SYS_OPERATE_NOTICE_COUNT);
            noticeVO.setReduceDataId(saveContract.getId());
            Integer operateType=convertSourceTypeToOperateType(saveVO.getBasicsVO().getSourceType());
            noticeVO.setReduceOperateType(operateType);
        }
        // 如果是新增数据，
        if (isSave) {
            // 启动外部流程
            SimpleTaskCompleteVO taskResult = processFeignService.startSimpleProcess(saveContract.getPartyAMemberId(), saveContract.getPartyARoleId(), saveContract.getOuterTaskType(), saveContract.getId());
            saveContract.setOuterTaskId(taskResult.getTaskId());
            saveContract.setOuterTaskStep(taskResult.getStep());
            saveContract.setOuterStatus(taskResult.getStatus());
            // 保存外部工作流信息
            repository.saveAndFlush(saveContract);
            //新增外部流转记录
            outerRecordService.add(user, saveContract.getId(), saveContract.getOuterStatus(), ContractOuterStatusEnum.getMessage(saveContract.getOuterStatus()),ContractActionEnum.ADD.getMessage(),  "");
            // 新增内部流转记录
            innerRecordService.add(user, saveContract.getId(), saveContract.getPartyAInnerStatus(), ContractPartyAInnerStatusEnum.getMessage(saveContract.getPartyAInnerStatus()), ContractActionEnum.ADD.getMessage(), "");
            //状态统计数变更
            noticeVO.setMemberId(saveContract.getPartyAMemberId());
            noticeVO.setRoleId(saveContract.getPartyARoleId());
            noticeVO.setDataSource(OperateDataSourceEnum.CONTRACT_MANAGE.getCode());
            noticeVO.setIncreaseOperateType(ContractManageOperateTypeEnum.TO_BE_COMMIT.getCode());
            noticeVO.setIncreaseCount(com.ssy.lingxi.contract.constant.Constants.SYS_OPERATE_NOTICE_COUNT);
            noticeVO.setIncreaseTitle(saveContract.getContractAbstract());
            noticeVO.setIncreaseDataId(saveContract.getId());
            feignService.addSysOperateNoticeToMQ(noticeVO);
            //发送消息
            sendContractSystemMessageInner(saveContract, MessageTemplateCode.contract_manage_wait_submit);
        }
        return Wrapper.success(saveContract.getId());
    }

    private Integer convertSourceTypeToOperateType(Integer sourceType){
        Integer operateType=null;
        switch (sourceType){
            case 1: //询价
                operateType=ContractManageOperateTypeEnum.TO_BE_CREATE_INQUIRY.getCode();
                break;
            case 2: //招标
                operateType=ContractManageOperateTypeEnum.TO_BE_CREATE_INVITE_TENDER.getCode();
                break;
            case 3: //竞价
                operateType=ContractManageOperateTypeEnum.TO_BE_CREATE_VIE_PRICE.getCode();
                break;
        }
        return operateType;
    }

    /**
     * 转换合同DO
     * @author 伍将
     * @date 2021/2/25
     * @param user: 当前登录用户
     * @param basicsVO: 合同基础信息VO
     * @return 合同DO对象
     **/
    private ContractDO convertContractDO(UserLoginCacheDTO user, ContractBasicsUpdateVO basicsVO) {
        ContractDO contract = new ContractDO();
        contract.setId(basicsVO.getId() > 0 ? basicsVO.getId() : null);
        contract.setPartyAMemberId(user.getMemberId());
        contract.setPartyARoleId(user.getMemberRoleId());
        contract.setPartyAName(user.getCompany());
        contract.setPartyBMemberId(basicsVO.getPartyBMemberId());
        contract.setPartyBRoleId(basicsVO.getPartyBRoleId());
        contract.setPartyBName(basicsVO.getPartyBName());
        contract.setContractNo(basicsVO.getContractNo());
        contract.setContractAbstract(basicsVO.getContractAbstract());
        contract.setStartTime(DateUtil.parse(basicsVO.getStartTime()).getTime());
        contract.setEndTime(DateUtil.parse(basicsVO.getEndTime()).getTime());
        contract.setTotalAmount(basicsVO.getTotalAmount());
        contract.setFreeAmount(basicsVO.getTotalAmount());
        contract.setExecuteAmount(0D);
        contract.setPayAmount(0D);
        contract.setUnPayApplyAmount(0D);
        contract.setUnApplyAmount(basicsVO.getTotalAmount());
        contract.setSourceType(basicsVO.getSourceType());
        contract.setSourceId(basicsVO.getSourceId());
        contract.setSourceNo(basicsVO.getSourceNo());
        contract.setOuterTaskType(ProcessEnum.CONTRACT_OUTER_VERIFY.getCode());
        contract.setOuterStatus(ContractOuterStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode());
        contract.setOuterTaskStep(0);
        contract.setPartyAInnerTaskType(ProcessEnum.CONTRACT_PARTY_A_INNER_VERIFY.getCode());
        contract.setPartyAInnerStatus(ContractPartyAInnerStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode());
        contract.setPartyAInnerTaskStep(0);
        contract.setPartyBInnerTaskType(ProcessEnum.CONTRACT_PARTY_B_INNER_VERIFY.getCode());
        contract.setPartyBInnerStatus(ContractPartyBInnerStatusEnum.TO_BE_SUBMIT_EXAMINE.getCode());
        contract.setPartyBInnerTaskStep(0);
        contract.setOldContractId(basicsVO.getOldContractId());
        contract.setIsSubmit(CommonBooleanEnum.NO.getCode());
        contract.setInvalidTime(0L);
        contract.setInvalidReason("");
        contract.setCreateTime(System.currentTimeMillis());
        contract.setUpdateTime(System.currentTimeMillis());
        return contract;
    }

    /**
     * 分页查询待提新增合同列表
     * @author 伍将
     * @date 2021/2/3
     * @param user: 当前登录用户
     * @param pageVO: 分页请求参数
     * @return 操作结果
     **/
    @Override
    public Wrapper<PageData<ContractManageQueryVO>> pageToBeAdd(UserLoginCacheDTO user, ContractManageCommonPageVO pageVO) {
        ContractManagePageVO managePageVO = new ContractManagePageVO();
        BeanUtils.copyProperties(pageVO, managePageVO);
        List<Integer> outerStatusList = new ArrayList<>();
        outerStatusList.add(ContractOuterStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode());
        outerStatusList.add(ContractOuterStatusEnum.PARTY_B_DISAGREE_SIGN_CONTRACT.getCode());
        outerStatusList.add(ContractOuterStatusEnum.PARTY_A_DISAGREE_SIGN_CONTRACT.getCode());
        return commonPageList(user, managePageVO, outerStatusList, null);
    }

    /**
     * 提交合同
     * @author 伍将
     * @date 2021/2/3
     * @param user: 当前登录用户
     * @param submitVO: 提交请求参数
     * @return 操作结果
     **/
    @Transactional
    @Override
    public Wrapper<Void> submit(UserLoginCacheDTO user, ContractManageSubmitVO submitVO) {

        ContractDO contract = repository.findById(submitVO.getContractId()).orElse(null);
        if (null == contract) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
        }

        // 外部状态为待提交乙方签订合同、内部状态为待提交乙方签订合同 或者 甲方不同意签订合同 或 乙方不同意签订合同，才允许提交合同
        if ((!ContractOuterStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode().equals(contract.getOuterStatus())
                || !ContractPartyAInnerStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode().equals(contract.getPartyAInnerStatus()))
        &&!ContractOuterStatusEnum.PARTY_B_DISAGREE_SIGN_CONTRACT.getCode().equals(contract.getOuterStatus())
        &&!ContractOuterStatusEnum.PARTY_A_DISAGREE_SIGN_CONTRACT.getCode().equals(contract.getOuterStatus())) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_SUBMIT);
        }
        //判断是否为电子合同
        ContractTextVO contractTextVO = contractTextService.getDetail(contract.getId()).getData();
        if(CommonBooleanEnum.YES.getCode().equals(contractTextVO.getIsUseElectronicContract())){
            SignFlowBO signFlowBO = contractSignatureService.signatureFlowCreate(contract.getPartyAMemberId(), contract.getPartyBMemberId(), contractTextVO.getContractName(), contractTextVO.getContractUrl());
            contract.setSignatureLogId(signFlowBO.getSignatureLogId());
        }
        // 执行外部工作流
        contract=processFeignService.completeSimpleTaskByContractOuter(contract, ProcessTaskStatusEnum.GOTO_NEXT_STEP.getCode());
        contract.setPartyAInnerStatus(ContractPartyAInnerStatusEnum.TO_BE_PARTY_B_SIGN_CONTRACT.getCode());

        // 启动乙方内部流程
        SimpleTaskCompleteVO taskResult = processFeignService.startSimpleProcess(contract.getPartyBMemberId(), contract.getPartyBRoleId(), contract.getPartyBInnerTaskType(), contract.getId());
        contract.setPartyBInnerTaskId(taskResult.getTaskId());
        contract.setPartyBInnerTaskStep(taskResult.getStep());
        contract.setPartyBInnerStatus(taskResult.getStatus());

        contract.setOuterStatus(ContractOuterStatusEnum.TO_BE_PARTY_B_SIGN_CONTRACT.getCode());
        contract.setPartyAInnerStatus(ContractPartyAInnerStatusEnum.TO_BE_PARTY_B_SIGN_CONTRACT.getCode());
        contract.setIsSubmit(CommonBooleanEnum.YES.getCode());
        contract.setUpdateTime(System.currentTimeMillis());

        repository.saveAndFlush(contract);

        //新增外部流转记录
        outerRecordService.add(user,contract.getId(),contract.getOuterStatus(),ContractOuterStatusEnum.getMessage(contract.getOuterStatus()),ContractActionEnum.SUBMIT.getMessage(),"");
        // 新增内部流转记录
        innerRecordService.add(user, contract.getId(), contract.getPartyAInnerStatus(), ContractPartyAInnerStatusEnum.getMessage(contract.getPartyAInnerStatus()), ContractActionEnum.SUBMIT_VALIFY.getMessage(), "");

        // 待提交乙方签订合同-1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(contract.getPartyAMemberId());
        noticeVO.setRoleId(contract.getPartyARoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.CONTRACT_MANAGE.getCode());
        noticeVO.setReduceCount(com.ssy.lingxi.contract.constant.Constants.SYS_OPERATE_NOTICE_COUNT);
        noticeVO.setReduceDataId(contract.getId());
        noticeVO.setReduceOperateType(ContractManageOperateTypeEnum.TO_BE_COMMIT.getCode());
        //待乙方签订合同+1
        OperateNoticeVO addNoticeVO = new OperateNoticeVO();
        addNoticeVO.setMemberId(contract.getPartyBMemberId());
        addNoticeVO.setRoleId(contract.getPartyBRoleId());
        addNoticeVO.setDataSource(OperateDataSourceEnum.CONTRACT_COORDINATION.getCode());
        addNoticeVO.setIncreaseCount(com.ssy.lingxi.contract.constant.Constants.SYS_OPERATE_NOTICE_COUNT);
        addNoticeVO.setIncreaseDataId(contract.getId());
        addNoticeVO.setIncreaseOperateType(ContractCoordinationOperateTypeEnum.TO_BE_COMMIT_VALIFY.getCode());
        addNoticeVO.setIncreaseTitle(contract.getContractAbstract());
        feignService.addSysOperateNoticeToMQ(Stream.of(noticeVO,addNoticeVO).collect(Collectors.toList()));
        //发送消息
        sendContractSystemMessageOuter(contract, MessageTemplateCode.contract_synergy_wait_audit);
        return Wrapper.success();
    }

    /**
     * 删除合同
     * @author 伍将
     * @date 2021/2/3
     * @param user: 当前登录用户
     * @param deleteVO: 删除请求参数
     * @return 操作结果
     **/
    @Transactional
    @Override
    public Wrapper<Void> delete(UserLoginCacheDTO user, ContractManageDeleteVO deleteVO) {

        ContractDO contract = repository.findById(deleteVO.getContractId()).orElse(null);
        if (null == contract) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
        }
        // 只有内部状态为待提交乙方签订合同状态且从未提交过的才可以删除
        if (!ContractOuterStatusEnum.TO_BE_SUBMIT_PARTY_B_SIGN_CONTRACT.getCode().equals(contract.getOuterStatus())
                || !CommonBooleanEnum.NO.getCode().equals(contract.getIsSubmit())) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_DELETE);
        }

        // 删除合同
        repository.deleteById(contract.getId());

        // 删除合同文本
        contractTextService.deleteByContractId(contract.getId());

        // 删除付款计划
        payPlanService.deleteByContractId(contract.getId());

        // 删除采购物料
        purchaseMaterielService.deleteByContractId(contract.getId());
        ContractManageOperateTypeEnum increaseEnum=null;
        if(contract.getSourceId()!=null&&contract.getSourceId()>0&&contract.getSubContractId()==0) {
            Wrapper<Void> wrapper = null;
            // 更新采购询价合同未被创建合同
            if (ContractSourceTypeEnum.PURCHASE_INQUIRY.getCode().equals(contract.getSourceType())) {
                wrapper = purchaseInquiryService.updateCreateStatus(user, contract.getSourceId(), contract.getPartyBMemberId(), contract.getPartyBRoleId(), CommonBooleanEnum.NO.getCode());
                increaseEnum=ContractManageOperateTypeEnum.TO_BE_CREATE_INQUIRY;
            }
            // 更新采购招标合同未被创建合同
            else if (ContractSourceTypeEnum.PURCHASE_INVITE_BID.getCode().equals(contract.getSourceType())) {
                wrapper = purchaseInviteBidService.updateCreateStatus(user, contract.getSourceId(), contract.getPartyBMemberId(), contract.getPartyBRoleId(), CommonBooleanEnum.NO.getCode());
                increaseEnum=ContractManageOperateTypeEnum.TO_BE_CREATE_INVITE_TENDER;
            }
            // 更新采购竞价合同未被创建合同
            else if (ContractSourceTypeEnum.PURCHASE_VIA_PRICE.getCode().equals(contract.getSourceType())) {
                wrapper = purchaseViePriceService.updateCreateStatus(user, contract.getSourceId(), contract.getPartyBMemberId(), contract.getPartyBRoleId(), CommonBooleanEnum.NO.getCode());
                increaseEnum=ContractManageOperateTypeEnum.TO_BE_CREATE_VIE_PRICE;
            }
            if (null == wrapper || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                return Optional.ofNullable(wrapper).orElse(Wrapper.fail(ResponseCode.BUSINESS_ERROR));
            }
        }
        //待提交审核合同-1 ，如果非手动且变更末单,对应询价/竞价/招标单据加+1
        addNotice(contract,ContractManageOperateTypeEnum.TO_BE_COMMIT,increaseEnum);
        return Wrapper.success();
    }

    /**
     * 分页查询待提交审核合同列表
     * @author 伍将
     * @date 2021/2/2
     * @param user: 当前登录用户
     * @param pageVO: 分页请求参数
     * @return 操作结果
     **/
    @Override
    public Wrapper<PageData<ContractManageQueryVO>> pageToBeSubmitExamine(UserLoginCacheDTO user, ContractManageCommonPageVO pageVO) {
        ContractManagePageVO managePageVO = new ContractManagePageVO();
        BeanUtils.copyProperties(pageVO, managePageVO);
        managePageVO.setOuterStatus(ContractOuterStatusEnum.TO_BE_PARTY_A_SIGN_CONTRACT.getCode());
        managePageVO.setInnerStatus(ContractPartyAInnerStatusEnum.TO_BE_SUBMIT_EXAMINE.getCode());
        return commonPageList(user, managePageVO, null, null);
    }

    /**
     * 添加系统操作通知-mq
     * @param contract 合同
     * @param reduceOperateType 合同管理操作类型枚举
     * @param increaseOperateType 合同管理操作类型枚举
     */
    private void addNotice(ContractDO contract,ContractManageOperateTypeEnum reduceOperateType,ContractManageOperateTypeEnum increaseOperateType){
        OperateNoticeVO addNoticeVO = new OperateNoticeVO();
        addNoticeVO.setMemberId(contract.getPartyAMemberId());
        addNoticeVO.setRoleId(contract.getPartyARoleId());
        addNoticeVO.setDataSource(OperateDataSourceEnum.CONTRACT_MANAGE.getCode());
        if(increaseOperateType!=null) {
            addNoticeVO.setIncreaseCount(com.ssy.lingxi.contract.constant.Constants.SYS_OPERATE_NOTICE_COUNT);
            addNoticeVO.setIncreaseDataId(contract.getId());
            addNoticeVO.setIncreaseOperateType(increaseOperateType.getCode());
            addNoticeVO.setIncreaseTitle(contract.getContractAbstract());
        }
        if(reduceOperateType!=null) {
            addNoticeVO.setReduceCount(com.ssy.lingxi.contract.constant.Constants.SYS_OPERATE_NOTICE_COUNT);
            addNoticeVO.setReduceDataId(contract.getId());
            addNoticeVO.setReduceOperateType(reduceOperateType.getCode());
        }
        feignService.addSysOperateNoticeToMQ(addNoticeVO);
    }

    /**
     * 提交审核
     * @author 伍将
     * @date 2021/2/3
     * @param user:当前登录用户
     * @param examineVO:审核请求参数
     * @return 操作结果
     **/
    @Transactional
    @Override
    public Wrapper<Void> submitExamine(UserLoginCacheDTO user, ContractManageExamineVO examineVO) {

        // 检查审核参数
        ContractDO contract = checkExamineParam(examineVO);

        // 外部状态为待甲方签订合同、内部状态为待提交审核合同，才允许提交审核
        if (!ContractOuterStatusEnum.TO_BE_PARTY_A_SIGN_CONTRACT.getCode().equals(contract.getOuterStatus())
                || !ContractPartyAInnerStatusEnum.TO_BE_SUBMIT_EXAMINE.getCode().equals(contract.getPartyAInnerStatus())) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_EXAMINE);
        }

        // 执行甲方内部工作流
        contract = processFeignService.completeSimpleTaskByContractPartyAInner(contract, examineVO.getIsPass());
        String statusName=ContractPartyAInnerStatusEnum.getMessage(contract.getPartyAInnerStatus());
        // 新增内部流转记录
        innerRecordService.add(user, contract.getId(), contract.getPartyAInnerStatus(), statusName, ContractActionEnum.SUBMIT_VALIFY.getMessage(), examineVO.getOpinion());

        // 保存合同信息
        contract.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(contract);
        //待提交审核合同-1,待审核合同（一级）+1
        addNotice(contract,ContractManageOperateTypeEnum.TO_BE_COMMIT_VALIFY,ContractManageOperateTypeEnum.TO_BE_VALIFY_STEP1);

        //发送消息
        sendContractSystemMessageInner(contract,MessageTemplateCode.contract_manage_audit_step_1);
        return Wrapper.success();
    }

    /**
     * 分页查询待审核一级合同列表
     * @author 伍将
     * @date 2021/2/2
     * @param user: 当前登录用户
     * @param pageVO: 分页请求参数
     * @return 操作结果
     **/
    @Override
    public Wrapper<PageData<ContractManageQueryVO>> pageToBeExamineStepOne(UserLoginCacheDTO user, ContractManageCommonPageVO pageVO) {
        ContractManagePageVO managePageVO = new ContractManagePageVO();
        BeanUtils.copyProperties(pageVO, managePageVO);
        managePageVO.setOuterStatus(ContractOuterStatusEnum.TO_BE_PARTY_A_SIGN_CONTRACT.getCode());
        managePageVO.setInnerStatus(ContractPartyAInnerStatusEnum.TO_BE_EXAMINE_1.getCode());
        return commonPageList(user, managePageVO, null, null);
    }

    /**
     * 一级审核
     * @author 伍将
     * @date 2021/2/3
     * @param user:当前登录用户
     * @param examineVO:审核请求参数
     * @return 操作结果
     **/
    @Transactional
    @Override
    public Wrapper<Void> examineStepOne(UserLoginCacheDTO user, ContractManageExamineVO examineVO) {

        // 检查审核参数
        ContractDO contract = checkExamineParam(examineVO);

        // 外部状态为待甲方签订合同、内部状态为待提交审核合同，才允许提交审核
        if (!ContractOuterStatusEnum.TO_BE_PARTY_A_SIGN_CONTRACT.getCode().equals(contract.getOuterStatus())
                || !ContractPartyAInnerStatusEnum.TO_BE_EXAMINE_1.getCode().equals(contract.getPartyAInnerStatus())) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_EXAMINE);
        }

        // 执行甲方内部工作流
        contract = processFeignService.completeSimpleTaskByContractPartyAInner(contract, examineVO.getIsPass());
        String statusName=ContractPartyAInnerStatusEnum.getMessage(contract.getPartyAInnerStatus());
        // 新增内部流转记录
        innerRecordService.add(user, contract.getId(), contract.getPartyAInnerStatus(), statusName, ContractActionEnum.EXAMINE_1.getMessage(), examineVO.getOpinion());

        // 保存合同信息
        contract.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(contract);
        //待审核合同（一级）-1，待审核合同（二级）+1
        addNotice(contract,ContractManageOperateTypeEnum.TO_BE_VALIFY_STEP1,ContractManageOperateTypeEnum.TO_BE_VALIFY_STEP2);

        //发送消息
        sendContractSystemMessageInner(contract,MessageTemplateCode.contract_manage_audit_step_2);
        return Wrapper.success();
    }

    /**
     * 分页查询待审核二级合同列表
     * @author 伍将
     * @date 2021/2/2
     * @param user: 当前登录用户
     * @param pageVO: 分页请求参数
     * @return 操作结果
     **/
    @Override
    public Wrapper<PageData<ContractManageQueryVO>> pageToBeExamineStepTwo(UserLoginCacheDTO user, ContractManageCommonPageVO pageVO) {
        ContractManagePageVO managePageVO = new ContractManagePageVO();
        BeanUtils.copyProperties(pageVO, managePageVO);
        managePageVO.setOuterStatus(ContractOuterStatusEnum.TO_BE_PARTY_A_SIGN_CONTRACT.getCode());
        managePageVO.setInnerStatus(ContractPartyAInnerStatusEnum.TO_BE_EXAMINE_2.getCode());
        return commonPageList(user, managePageVO, null, null);
    }

    /**
     * 二级审核
     * @author 伍将
     * @date 2021/2/3
     * @param user:当前登录用户
     * @param examineVO:审核请求参数
     * @return 操作结果
     **/
    @Transactional
    @Override
    public Wrapper<Void> examineStepTwo(UserLoginCacheDTO user, ContractManageExamineVO examineVO) {

        // 检查审核参数
        ContractDO contract = checkExamineParam(examineVO);

        // 外部状态为待甲方签订合同、内部状态为待提交审核合同，才允许提交审核
        if (!ContractOuterStatusEnum.TO_BE_PARTY_A_SIGN_CONTRACT.getCode().equals(contract.getOuterStatus())
                || !ContractPartyAInnerStatusEnum.TO_BE_EXAMINE_2.getCode().equals(contract.getPartyAInnerStatus())) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_EXAMINE);
        }

        // 执行甲方内部工作流
        contract = processFeignService.completeSimpleTaskByContractPartyAInner(contract, examineVO.getIsPass());
        String statusName=ContractPartyAInnerStatusEnum.getMessage(contract.getPartyAInnerStatus());
        // 新增内部流转记录
        innerRecordService.add(user, contract.getId(), contract.getPartyAInnerStatus(), statusName, ContractActionEnum.EXAMINE_2.getMessage(), examineVO.getOpinion());

        // 保存合同信息
        contract.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(contract);
        //待审核合同（二级）-1，待签订合同+1
        addNotice(contract,ContractManageOperateTypeEnum.TO_BE_VALIFY_STEP2,ContractManageOperateTypeEnum.TO_BE_SIGN);
        //发送消息
        sendContractSystemMessageInner(contract,MessageTemplateCode.contract_manage_wait_sign);
        return Wrapper.success();
    }

    /**
     * 分页查询待签订合同列表
     * @author 伍将
     * @date 2021/2/2
     * @param user: 当前登录用户
     * @param pageVO: 分页请求参数
     * @return 操作结果
     **/
    @Override
    public Wrapper<PageData<ContractManageQueryVO>> pageToBeSign(UserLoginCacheDTO user, ContractManageCommonPageVO pageVO) {
        ContractManagePageVO managePageVO = new ContractManagePageVO();
        BeanUtils.copyProperties(pageVO, managePageVO);
        managePageVO.setOuterStatus(ContractOuterStatusEnum.TO_BE_PARTY_A_SIGN_CONTRACT.getCode());
        List<Integer> innerStatusList = new ArrayList<>();
        innerStatusList.add(ContractPartyAInnerStatusEnum.SUBMIT_EXAMINE_NO_PASS.getCode());
        innerStatusList.add(ContractPartyAInnerStatusEnum.EXAMINE_NO_PASS_1.getCode());
        innerStatusList.add(ContractPartyAInnerStatusEnum.EXAMINE_PASS_2.getCode());
        innerStatusList.add(ContractPartyAInnerStatusEnum.EXAMINE_NO_PASS_2.getCode());
        return commonPageList(user, managePageVO, null, innerStatusList);
    }

    /**
     * 签订合同
     * @author 伍将
     * @date 2021/2/7
     * @param user: 当前登录用户
     * @param examineVO: 审核请求参数
     * @return 操作结果
     **/
    @Transactional
    @Override
    public Wrapper<Void> sign(UserLoginCacheDTO user, ContractManageExamineVO examineVO) {

        // 检查审核参数
        ContractDO contract = checkExamineParam(examineVO);

        // 外部状态为待甲方签订合同、内部状态为待提交审核合同，才允许提交审核
        if (!ContractOuterStatusEnum.TO_BE_PARTY_A_SIGN_CONTRACT.getCode().equals(contract.getOuterStatus())
                || (!ContractPartyAInnerStatusEnum.EXAMINE_PASS_2.getCode().equals(contract.getPartyAInnerStatus())
                && !ContractPartyAInnerStatusEnum.EXAMINE_NO_PASS_2.getCode().equals(contract.getPartyAInnerStatus())
                && !ContractPartyAInnerStatusEnum.SUBMIT_EXAMINE_NO_PASS.getCode().equals(contract.getPartyAInnerStatus())
                && !ContractPartyAInnerStatusEnum.EXAMINE_NO_PASS_1.getCode().equals(contract.getPartyAInnerStatus()))) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_SIGN);
        }

        // 只有当内部状态为审核通过(二级)时，才允许选择同意签订
        if (CommonBooleanEnum.YES.getCode().equals(examineVO.getIsPass())
                && !ContractPartyAInnerStatusEnum.EXAMINE_PASS_2.getCode().equals(contract.getPartyAInnerStatus())) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_ALLOW_SIGN);
        }

        // 执行甲方内部工作流
        contract = processFeignService.completeSimpleTaskByContractPartyAInner(contract, examineVO.getIsPass());
        String innerStatusName=ContractPartyAInnerStatusEnum.getMessage(contract.getPartyAInnerStatus());
        // 新增内部流转记录
        innerRecordService.add(user, contract.getId(), contract.getPartyAInnerStatus(), innerStatusName, ContractActionEnum.SIGN.getMessage(), examineVO.getOpinion());

        // 执行外部工作流

        contract = processFeignService.completeSimpleTaskByContractOuter(contract, examineVO.getIsPass());
        String outerStatusName=ContractOuterStatusEnum.getMessage(contract.getOuterStatus());
        // 新增外部流转记录
        outerRecordService.add(user, contract.getId(), contract.getOuterStatus(), outerStatusName, ContractActionEnum.SIGN_PARTY_A.getMessage(), examineVO.getOpinion());

        // 如果旧合同id大于0，则属于变更合同，合同签订后，自动停用原合同
        if (contract.getOldContractId() > 0 && CommonBooleanEnum.YES.getCode().equals(examineVO.getIsPass())) {
            ContractDO oldContract = repository.findById(contract.getOldContractId()).orElse(null);
            if (null == oldContract) {
                throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
            }

            // 保存旧合同信息
            oldContract.setOuterStatus(ContractOuterStatusEnum.STOP_USING.getCode());
            oldContract.setPartyAInnerStatus(ContractPartyAInnerStatusEnum.STOP_USING.getCode());
            oldContract.setUpdateTime(System.currentTimeMillis());
            repository.saveAndFlush(oldContract);

            // 新增内部流转记录
            innerRecordService.add(user, oldContract.getId(), oldContract.getPartyAInnerStatus(), "已停用", "停用", examineVO.getOpinion());
            //变更完成，原合同作废通知商品服务 -- 新增通知就行了, 旧数据会被新数据覆盖, 否则历史数据会有多余
//            notifyProductServiceForContractChange(GoodsPriceStatusEnum.ABANDONED,oldContract);
        }

        //更新合同文件
        if(StringUtils.hasLength(examineVO.getContractUrl())) {
            contractTextService.updateUrlById(contract.getId(),examineVO.getContractUrl());
        }

        // 保存合同信息
        contract.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(contract);
        //待签订合同-1
        addNotice(contract,ContractManageOperateTypeEnum.TO_BE_SIGN,null);
        if(CommonBooleanEnum.NO.getCode().equals(examineVO.getIsPass())) {
            //待新增合同+1
            addNotice(contract, null, ContractManageOperateTypeEnum.TO_BE_COMMIT);
        }
        //完成签约通知商品服务
        notifyProductServiceForContractChange(GoodsPriceStatusEnum.NORMAL,contract);
        return Wrapper.success();
    }

    /**
     * 检查审核参数
     * @author 伍将
     * @date 2021/2/3
     * @param examineVO: 审核请求参数
     * @return 合同
     **/
    private ContractDO checkExamineParam(ContractManageExamineVO examineVO) {

        // 验证审核参数
        if (!CommonBooleanEnum.NO.getCode().equals(examineVO.getIsPass()) && !CommonBooleanEnum.YES.getCode().equals(examineVO.getIsPass())) {
            throw new BusinessException(ResponseCode.CONTRACT_EXAMINE_PARAM_ERROR);
        }
        // 审核原因不能为空
        if (CommonBooleanEnum.NO.getCode().equals(examineVO.getIsPass()) && !StringUtils.hasLength(examineVO.getOpinion())) {
            throw new BusinessException(ResponseCode.CONTRACT_EXAMINE_OPINION_EMPTY);
        }

        ContractDO contract = repository.findById(examineVO.getContractId()).orElse(null);
        if (null == contract) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
        }

        return contract;
    }

    /**
     *  公共分页查询合同列表方法
     * @author 伍将
     * @date 2021/2/2
     * @param user: 当前登录用户
     * @param pageVO: 分页请求参数
     * @param outerStatusList: 外部状态列表
     * @param innerStatusList: 内部状态列表
     * @return 操作结果
     **/
    private Wrapper<PageData<ContractManageQueryVO>> commonPageList(UserLoginCacheDTO user, ContractManagePageVO pageVO, List<Integer> outerStatusList, List<Integer> innerStatusList) {

        // 组装查询条件
        Specification<ContractDO> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("partyAMemberId").as(Long.class), user.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("partyARoleId").as(Long.class), user.getMemberRoleId()));
            // 组装外部状态条件
            if (null != outerStatusList && outerStatusList.size() > 0) {
                list.add(criteriaBuilder.in(root.get("outerStatus")).value(outerStatusList));
            } else {
                if (null != pageVO.getOuterStatus() && !ContractOuterStatusEnum.ALL.getCode().equals(pageVO.getOuterStatus())) {
                    list.add(criteriaBuilder.equal(root.get("outerStatus").as(Integer.class), pageVO.getOuterStatus()));
                }
            }
            // 组装内部状态条件
            if (null != innerStatusList && innerStatusList.size() > 0) {
                list.add(criteriaBuilder.in(root.get("partyAInnerStatus")).value(innerStatusList));
            } else {
                if (null != pageVO.getInnerStatus() && !ContractPartyAInnerStatusEnum.ALL.getCode().equals(pageVO.getInnerStatus())) {
                    list.add(criteriaBuilder.equal(root.get("partyAInnerStatus").as(Integer.class), pageVO.getInnerStatus()));
                }
            }
            if (StringUtils.hasLength(pageVO.getStartTime())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("startTime").as(Long.class), DateUtil.parse(pageVO.getStartTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getEndTime())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("endTime").as(Long.class), DateTimeUtil.toMilliSecond(DateTimeUtil.parseDateEnd(pageVO.getEndTime()))));
            }
            if (StringUtils.hasLength(pageVO.getContractNo())) {
                list.add(criteriaBuilder.like(root.get("contractNo").as(String.class), "%" + pageVO.getContractNo().trim() + "%"));
            }
            if (StringUtils.hasLength(pageVO.getContractAbstract())) {
                list.add(criteriaBuilder.like(root.get("contractAbstract").as(String.class), "%" + pageVO.getContractAbstract().trim() + "%"));
            }
            if (StringUtils.hasLength(pageVO.getPartyBName())) {
                list.add(criteriaBuilder.like(root.get("partyBName").as(String.class), "%" + pageVO.getPartyBName().trim() + "%"));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        // 组装分页参数
        Pageable page = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("startTime").descending());

        Page<ContractDO> result = repository.findAll(spec, page);
        List<ContractManageQueryVO> resultList = result.getContent().stream().map(o -> {
            ContractManageQueryVO entity = new ContractManageQueryVO();
            entity.setId(o.getId());
            entity.setContractNo(o.getContractNo());
            entity.setContractAbstract(o.getContractAbstract());
            entity.setStartTime(DateUtil.format(DateUtil.date(o.getStartTime()), "yyyy-MM-dd"));
            entity.setEndTime(DateUtil.format(DateUtil.date(o.getEndTime()), "yyyy-MM-dd"));
            entity.setPartyBName(o.getPartyBName());
            entity.setTotalAmount(o.getTotalAmount());
            entity.setSourceType(o.getSourceType());
            entity.setSourceTypeName(ContractSourceTypeEnum.getNameByCode(o.getSourceType()));
            entity.setSourceId(o.getSourceId());
            entity.setSourceNo(o.getSourceNo());
            entity.setOuterStatus(o.getOuterStatus());
            entity.setOuterStatusName(ContractOuterStatusEnum.getMessage(o.getOuterStatus()));
            entity.setInnerStatus(o.getPartyAInnerStatus());
            entity.setInnerStatusName(ContractPartyAInnerStatusEnum.getMessage(o.getPartyAInnerStatus()));
            entity.setPartyAMemberId(o.getPartyAMemberId());
            entity.setPartyARoleId(o.getPartyARoleId());
            entity.setPartyBMemberId(o.getPartyBMemberId());
            entity.setPartyBRoleId(o.getPartyBRoleId());
            entity.setTurn(o.getTurn());
            entity.setSubContractId(o.getSubContractId());
            entity.setSignatureLogId(o.getSignatureLogId());
            return entity;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(), resultList));
    }

    /**
     * 获取外部状态列表
     * @author 伍将
     * @date 2021/2/2
     * @return 操作结果
     **/
    @Override
    public Wrapper<List<PageItemVO>> getOuterStatusList() {
        return Wrapper.success(Arrays.stream(ContractOuterStatusEnum.values()).map(r -> {
                    PageItemVO status = new PageItemVO();
                    status.setStatus(r.getCode());
                    status.setName(r.getMessage());
                    return status;
                }).collect(Collectors.toList())
        );
    }

    /**
     * 获取内部状态列表
     * @author 伍将
     * @date 2021/2/2
     * @return 操作结果
     **/
    @Override
    public Wrapper<List<PageItemVO>> getInnerStatusList() {
        return Wrapper.success(Arrays.stream(ContractPartyAInnerStatusEnum.values()).map(r -> {
                    PageItemVO status = new PageItemVO();
                    status.setStatus(r.getCode());
                    status.setName(r.getMessage());
                    return status;
                }).collect(Collectors.toList())
        );
    }

    /**
     * 分页查询完成合同列表
     * @author 伍将
     * @date 2021/3/11
     * @param user: 当前登录用户
     * @param pageVO: 分页请求参数
     * @return 操作结果
     **/
    @Override
    public Wrapper<PageData<ContractCompleteQueryVO>> pageCompleteList(UserLoginCacheDTO user, ContractManageCommonPageVO pageVO) {

        // 组装查询条件
        Specification<ContractDO> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("partyAMemberId").as(Long.class), user.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("partyARoleId").as(Long.class), user.getMemberRoleId()));
            list.add(criteriaBuilder.equal(root.get("outerStatus").as(Integer.class), ContractOuterStatusEnum.COMPLETE_SIGN_CONTRACT.getCode()));
            if (StringUtils.hasLength(pageVO.getStartTime())) {
                list.add(criteriaBuilder.greaterThan(root.get("startTime").as(Long.class), DateUtil.parse(pageVO.getStartTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getEndTime())) {
                list.add(criteriaBuilder.lessThan(root.get("endTime").as(Long.class), DateUtil.parse(pageVO.getEndTime()).getTime()));
            }
            if (StringUtils.hasLength(pageVO.getContractNo())) {
                list.add(criteriaBuilder.like(root.get("contractNo").as(String.class), "%" + pageVO.getContractNo().trim() + "%"));
            }
            if (StringUtils.hasLength(pageVO.getContractAbstract())) {
                list.add(criteriaBuilder.like(root.get("contractAbstract").as(String.class), "%" + pageVO.getContractAbstract().trim() + "%"));
            }
            if (StringUtils.hasLength(pageVO.getPartyBName())) {
                list.add(criteriaBuilder.like(root.get("partyBName").as(String.class), "%" + pageVO.getPartyBName().trim() + "%"));
            }
            if (pageVO.getSourceType()!=null) {
                list.add(criteriaBuilder.equal(root.get("sourceType").as(Integer.class), pageVO.getSourceType()));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        // 组装分页参数
        Pageable page = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("startTime").descending());

        Page<ContractDO> result = repository.findAll(spec, page);
        List<ContractCompleteQueryVO> resultList = result.getContent().stream().map(o -> {
            ContractCompleteQueryVO entity = new ContractCompleteQueryVO();
            entity.setId(o.getId());
            entity.setContractNo(o.getContractNo());
            entity.setContractAbstract(o.getContractAbstract());
            entity.setStartTime(DateUtil.format(DateUtil.date(o.getStartTime()), "yyyy-MM-dd"));
            entity.setEndTime(DateUtil.format(DateUtil.date(o.getEndTime()), "yyyy-MM-dd"));
            entity.setPartyBName(o.getPartyBName());
            entity.setPartyBMemberId(o.getPartyBMemberId());
            entity.setPartyBRoleId(o.getPartyBRoleId());
            entity.setTotalAmount(o.getTotalAmount());
            entity.setSourceType(o.getSourceType());
            entity.setSourceTypeName(ContractSourceTypeEnum.getNameByCode(o.getSourceType()));
            entity.setSourceId(o.getSourceId());
            entity.setSourceNo(o.getSourceNo());
            entity.setFreeAmount(o.getFreeAmount());
            entity.setPurchaseType(o.getPurchaseType());
            return entity;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(), resultList));
    }

    private String fillZero(Integer count){
       return org.apache.commons.lang.StringUtils.leftPad(String.valueOf(count), com.ssy.lingxi.contract.constant.Constants.CONTRACT_NO_LENGTH, '0');
    }

    /**
     * 获取新合同编号
     * @author 伍将
     * @date 2021/3/26
     * @param oldContractNo: 旧合同编号
     * @return 操作结果
     **/
    @Override
    public Wrapper<String> getContractNo(String oldContractNo) {

        // 最长16字符，自动生成后可修改，自动生成规则为H-2位年份-5位从00001开始的序列号:H-13-00001
        StringBuilder result = new StringBuilder();

        // 如果oldContractNo不为空，则为变更合同
        if (StringUtils.hasLength(oldContractNo)) {
            // 检查是否以_N为结尾字符串，是，则计数累计，否则首次变更，直接加“_1”
            String[] str = oldContractNo.split("_");
            if (str.length > 1) {
                for (int i = 0; i < str.length - 1; i++) {
                    result.append(str[i]);
                }
                result.append("_").append(Integer.parseInt(str[str.length - 1]) + 1);
            } else {
                result = new StringBuilder((oldContractNo + "_1"));
            }
        } else {
            // 生成当前年份合同编号
            String year = new SimpleDateFormat("yy", Locale.CHINESE).format(new Date());
            String contractNo = redisStringUtils.get(Constants.REDIS_KEY_CONTRACT_NO, Constants.REDIS_CONTRACT_INDEX);
            if(StringUtils.hasLength(contractNo)){
                contractNo = getValidContractNo(year);
            }else {
                //String tradeCode = SerialNumberUtils.getNumber(redisStringUtils, Constants.CREDIT_TRACE_CODE, com.ssy.lingxi.common.constant.Constants.REDIS_ORDER_INDEX, Constants.CREDIT_TRACE_CODE_DATE, Constants.CREDIT_TRACE_CODE_NUM_LEN);
                // 查询当前年份已生成合同数
                Date nowTime = DateUtil.date();
                Long startTime = DateUtil.parse(DateUtil.format(nowTime, "yyyy-01-01")).getTime();
                Long endTime = DateUtil.parse(DateUtil.format(nowTime, "yyyy-12-31 23:59:59")).getTime();
                Integer count = repository.countByCreateTimeBetween(startTime, endTime);
                count++;
                contractNo = fillZero(count);
                redisStringUtils.set(Constants.REDIS_KEY_CONTRACT_NO,count.toString(), Constants.REDIS_CONTRACT_INDEX);
            }
            result = new StringBuilder(String.format("H-%s-%s", year, contractNo));
        }

        return Wrapper.success(result.toString());
    }

    /**
     * 循环判断合同编号是否存在.存在则递增后判断
     * @return 合同编码
     */
    private String getValidContractNo(String year){
        String contractNo = SerialNumberUtils.getPureNumber(redisStringUtils, Constants.REDIS_KEY_CONTRACT_NO, Constants.REDIS_CONTRACT_INDEX, com.ssy.lingxi.contract.constant.Constants.CONTRACT_NO_LENGTH);
        Boolean exists = repository.existsByContractNo(String.format("H-%s-%s", year, contractNo));
        if(exists){
            return getValidContractNo(year);
        }
        return contractNo;
    }

    public Wrapper<ContractManageInfoVO> getContractInfo(UserLoginCacheDTO user, Long contractId){
        ContractManageInfoVO result = new ContractManageInfoVO();

        ContractDO contract = repository.findById(contractId).orElse(null);
        if (null == contract) {
            throw new BusinessException(ResponseCode.CONTRACT_NOT_EXIST);
        }
        //查询收款方的对公账户信息
        Wrapper<CorporateAccountConfigResponse> corporateAccountConfig = feignService.getCorporateAccountConfig(contract.getPartyBMemberId(), contract.getPartyBRoleId());
        if (null == corporateAccountConfig || corporateAccountConfig.getCode() != ResponseCode.SUCCESS.getCode()) {
            throw new BusinessException(ResponseCode.CONTRACT_GET_CORPORATE_ACCOUNT_CONFIG_ERROR);
        }
        result.setCorporateAccountConfig(corporateAccountConfig.getData());
        // 获取支付计划
        Wrapper<List<ContractPayPlanVO>> payPlanListWrapper = payPlanService.getPayPlanList(contractId);
        if (null == payPlanListWrapper || payPlanListWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            throw new BusinessException(ResponseCode.CONTRACT_GET_PAY_PLAN_ERROR);
        }
        result.setPayPlanList(payPlanListWrapper.getData());
        return Wrapper.success(result);
    }

    @Transactional
    @Override
    public void updateContractFreeAmount(ContractOrderAmountLogRequest request) {
        //查询合同
        ContractDO contractDO = repository.findById(request.getContractId()).orElse(null);
        if(contractDO==null){
            log.error("合同订单：合同不存在，id:{}",request);
            return;
        }
        //保存合同订单金额变更记录
        ContractOrderAmountLogDO contractOrderAmountLogDO=new ContractOrderAmountLogDO();
        BeanUtils.copyProperties(request,contractOrderAmountLogDO);
        contractOrderAmountLogRepository.save(contractOrderAmountLogDO);
        //更新合同金额
        Double freeAmount = contractDO.getFreeAmount();
        Double executeAmount = contractDO.getExecuteAmount();
        if(ContractOrderChangeTypeEnum.ADD.getCode().equals(request.getType())){
            contractDO.setFreeAmount(NumberUtil.sub(freeAmount,request.getSumPrice()));
            contractDO.setExecuteAmount(NumberUtil.add(executeAmount,request.getSumPrice()));
        }else if(ContractOrderChangeTypeEnum.CANCEL.getCode().equals(request.getType())){
            contractDO.setFreeAmount(NumberUtil.add(freeAmount,request.getSumPrice()));
            contractDO.setExecuteAmount(NumberUtil.sub(executeAmount,request.getSumPrice()));
        }
        repository.saveAndFlush(contractDO);
        //更新合同物料数量
        contractPurchaseMaterielService.updateContractPurchaseMaterielCount(request.getContractId(),request.getContractOrderMaterielRequestList(),request.getType());
    }

    /**
     * 发送合同模板消息-外部
     * @param contractDO 合同
     * @param messageTemplateCode 消息模板代码
     */
    private void sendContractSystemMessageOuter(ContractDO contractDO,String messageTemplateCode){
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(contractDO.getPartyBMemberId());
        request.setRoleId(contractDO.getPartyBRoleId());
        request.setMessageNotice(messageTemplateCode);
        request.setParams(Stream.of(contractDO.getContractNo(),contractDO.getContractAbstract()).collect(Collectors.toList()));
        feignService.sendSystemMessage(request);
    }
    /**
     * 发送合同模板消息-内部
     * @param contractDO 合同
     * @param messageTemplateCode 消息模板代码
     */
    private void sendContractSystemMessageInner(ContractDO contractDO,String messageTemplateCode){
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(contractDO.getPartyAMemberId());
        request.setRoleId(contractDO.getPartyARoleId());
        request.setMessageNotice(messageTemplateCode);
        request.setParams(Stream.of(contractDO.getContractNo(),contractDO.getContractAbstract()).collect(Collectors.toList()));
        feignService.sendSystemMessage(request);
    }


    private Specification<ContractDO> buildQueryConditions(){
        // 组装查询条件
        return  (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.ge(root.get("endTime").as(Long.class), DateUtil.beginOfDay(DateUtil.offsetDay(DateUtil.date(),-2)).getTime()));
            list.add(criteriaBuilder.le(root.get("endTime").as(Long.class), DateUtil.endOfDay(DateUtil.yesterday()).getTime()));
            List<Integer> collect = Stream.of(ContractOuterStatusEnum.INVALID.getCode(),ContractOuterStatusEnum.STOP_USING.getCode(),ContractOuterStatusEnum.EXPIRE.getCode()).collect(Collectors.toList());
            list.add(criteriaBuilder.not(criteriaBuilder.in(root.get("outerStatus")).value(collect)));
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };
    }


    @Override
    @Transactional
    public void updateExpiredContract() {
        List<ContractDO> all = repository.findAll(buildQueryConditions());
        if(CollectionUtil.isNotEmpty(all)){
            all.forEach(contract ->{
                contract.setOuterStatus(ContractOuterStatusEnum.EXPIRE.getCode());
                contract.setUpdateTime(System.currentTimeMillis());
                log.info("[定时任务][合同]updateExpiredContract:contractId:{},contractNo:{}合同过期",contract.getId(),contract.getContractNo());
            });
            repository.saveAll(all);
            //合同过期通知商品服务
            all.forEach(contractDO -> {
                notifyProductServiceForContractChange(GoodsPriceStatusEnum.MATURITY,contractDO);
            });

            log.info("[定时任务][合同]过期状态更新{}条完成",all.size());
        }
    }

    /**
     * 合同签订、到期、作废通知商品服务
     * @param handle 合同状态枚举
     * @param contractDO 合同实体类
     */
    private void notifyProductServiceForContractChange(GoodsPriceStatusEnum handle, ContractDO contractDO){
        //查询合同物料
        List<ContractPurchaseMaterielDO> materielDOS = contractPurchaseMaterielService.selectGoodsByContractId(contractDO.getId());
        if(CollectionUtil.isEmpty(materielDOS)){
            log.warn("notifyProductServiceForContractChange合同id：{},物料信息为空",contractDO.getId());
            return;
        }
        List<MaterielPriceRequest> materielPrices = materielDOS.stream().map(materielDO ->{
            MaterielPriceRequest materielPriceRequest = new MaterielPriceRequest();
            materielPriceRequest.setCode(materielDO.getMaterielNo());
            materielPriceRequest.setAmount(materielDO.getPrice());
            return materielPriceRequest;
        }).collect(Collectors.toList());
        ContractPriceRequest request=new ContractPriceRequest();
        request.setContractNo(contractDO.getContractNo());
        request.setStartTime(contractDO.getStartTime());
        request.setEndTime(contractDO.getEndTime());
        request.setPartyAMemberId(contractDO.getPartyAMemberId());
        request.setPartyARoleId(contractDO.getPartyARoleId());
        request.setPartyAName(contractDO.getPartyAName());
        request.setPartyBMemberId(contractDO.getPartyBMemberId());
        request.setPartyBRoleId(contractDO.getPartyBRoleId());
        request.setPartyBName(contractDO.getPartyBName());
        request.setMaterielPrices(materielPrices);
        request.setStatus(handle.getCode());
        String json = JSONUtil.toJsonStr(request);
        log.info("notifyProductServiceForContractChange合同id：{},合同状态变更通知商品服务，json:{}",contractDO.getId(),json);
        rabbitMQUtils.sendMsg(ContractConstants.GOODS_PRICE_CHANGE_EXCHANGE,ContractConstants.GOODS_PRICE_CHANGE_ROUTING_KEY,json);
    }
}
